package com.vrv.es;

import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;

import java.lang.reflect.Method;
import java.util.concurrent.Callable;

public class ElasticsearchSearchInterceptor {

    // 使用volatile确保多线程环境下的可见性
    private static volatile Object logger;
    private static volatile boolean loggerInitialized = false;
    private static final Object lock = new Object();
    private static Boolean isSlf4jAvailable = null;

    @RuntimeType
    public static Object intercept(@Origin Method method,
                                   @AllArguments Object[] args,
                                   @SuperCall Callable<?> callable)
            throws Exception {
        Object result = callable.call();
        if ("search".equals(method.getName()) && args != null && args.length > 0) {
            for (Object arg : args) {
                // 完全通过反射处理，不直接引用Elasticsearch类
                if (arg != null && isElasticsearchSearchRequest(arg)) {
                    try {
                        String logMessage = extractLogMessage(arg);

                        // 使用System.out作为备用日志输出
                        System.out.println(logMessage);

                        // 尝试使用宿主应用的日志系统
                        logWithHostLogger(logMessage);
                    } catch (Exception e) {
                        String errorMsg = "解析Elasticsearch请求时出错: " + e.getMessage();
                        System.out.println(errorMsg);
                        logWithHostLogger(errorMsg);
                    }
                }
            }
        }
        return result;
    }

    /**
     * 通过类名判断是否为Elasticsearch的SearchRequest对象
     */
    private static boolean isElasticsearchSearchRequest(Object obj) {
        return obj.getClass().getName().equals("org.elasticsearch.action.search.SearchRequest") ||
                obj.getClass().getName().equals("org.elasticsearch.action.search.SearchRequestBuilder");
    }

    /**
     * 通过反射提取日志信息
     */
    private static String extractLogMessage(Object searchRequest) throws Exception {
        StringBuilder logMessage = new StringBuilder("ElasticsearchSearchInterceptor : ");

        try {
            // 获取索引信息
            Method indicesMethod = searchRequest.getClass().getMethod("indices");
            Object indicesObj = indicesMethod.invoke(searchRequest);
            if (indicesObj instanceof String[]) {
                String[] indices = (String[]) indicesObj;
                logMessage.append(String.join(",", indices));
            } else {
                logMessage.append("unknown");
            }
        } catch (Exception e) {
            logMessage.append("unknown");
        }

        logMessage.append(" ");

        try {
            // 获取查询源信息
            Method sourceMethod = searchRequest.getClass().getMethod("source");
            Object sourceObj = sourceMethod.invoke(searchRequest);
            if (sourceObj != null) {
                // 尝试调用toString方法获取可读的查询信息
                Method toStringMethod = sourceObj.getClass().getMethod("toString");
                logMessage.append(toStringMethod.invoke(sourceObj));
            } else {
                logMessage.append("null");
            }
        } catch (Exception e) {
            // 如果无法获取source，尝试直接对searchRequest调用toString
            try {
                Method toStringMethod = searchRequest.getClass().getMethod("toString");
                logMessage.append(toStringMethod.invoke(searchRequest));
            } catch (Exception ex) {
                logMessage.append("无法获取查询详情");
            }
        }

        return logMessage.toString();
    }

    /**
     * 检查SLF4J是否在宿主应用中可用
     */
    private static boolean isSlf4jAvailable() {
        if (isSlf4jAvailable == null) {
            try {
                Class.forName("org.slf4j.LoggerFactory", false, Thread.currentThread().getContextClassLoader());
                Class.forName("org.slf4j.Logger", false, Thread.currentThread().getContextClassLoader());
                isSlf4jAvailable = true;
            } catch (ClassNotFoundException e) {
                isSlf4jAvailable = false;
                System.out.println("SLF4J类未在宿主应用中找到: " + e.getMessage());
            }
        }
        return isSlf4jAvailable;
    }

    /**
     * 尝试使用宿主应用的日志系统进行日志记录
     * 通过反射方式获取宿主应用的Logger，避免在Agent中直接引用slf4j
     */
    private static void logWithHostLogger(String message) {
        // 首先检查SLF4J是否可用
        if (!isSlf4jAvailable()) {
            System.out.println("[DEBUG] SLF4J在宿主应用中不可用，跳过SLF4J日志记录");
            return;
        }

        try {
            // 双重检查锁定确保只初始化一次
            if (!loggerInitialized) {
                synchronized (lock) {
                    if (!loggerInitialized) {
                        System.out.println("[DEBUG] 尝试初始化SLF4J日志系统");
                        // 通过反射获取LoggerFactory类和getLogger方法
                        Class<?> loggerFactoryClass = Class.forName("org.slf4j.LoggerFactory", false, Thread.currentThread().getContextClassLoader());
                        Method getLoggerMethod = loggerFactoryClass.getMethod("getLogger", String.class);

                        // 获取logger实例，使用字符串名称而不是Class对象
                        logger = getLoggerMethod.invoke(null, "com.vrv.es.ElasticsearchSearchInterceptor");
                        loggerInitialized = true;
                        System.out.println("[DEBUG] SLF4J日志系统初始化成功");
                    }
                }
            }

            if (logger != null) {
                // 通过反射调用warn方法记录日志
                Class<?> loggerClass = logger.getClass();
                Method warnMethod = loggerClass.getMethod("warn", String.class);
                warnMethod.invoke(logger, "[ES-AGENT] " + message);
                System.out.println("[DEBUG] 已通过SLF4J记录日志到文件: " + message);
            } else {
                System.out.println("[DEBUG] Logger实例为空，无法通过SLF4J记录日志");
            }
        } catch (Exception e) {
            System.out.println("[DEBUG] 通过SLF4J记录日志时出错: " + e.getMessage());
            e.printStackTrace();
        }
    }
}
